<?php

class Benservi_Boleto_BancoBrasil{
     
     public $dadosboleto = array();
     public $codigo_banco_com_dv;

     public function init(){}

     public function factory()
     {
        $codigobanco = "001";
        $codigo_banco_com_dv = $this->geraCodigoBanco($codigobanco);
        $this->codigo_banco_com_dv = $codigo_banco_com_dv;
        $nummoeda = "9";
         
        
        // ---------------------- FIM CONFIGURAÇÃO DO SEU BOLETO --------------- //

        $fator_vencimento = $this->fator_vencimento($this->dadosboleto["data_vencimento"]);

        //valor tem 10 digitos, sem virgula
        $valor = $this->formata_numero($this->dadosboleto["valor_boleto"],10,0,"valor");

        //agencia é sempre 4 digitos
        $agencia =  $this->formata_numero($this->dadosboleto["agencia"],4,0);
        //conta é sempre 8 digitos
        $conta =  $this->formata_numero($this->dadosboleto["conta"],8,0);
        //carteira 18
        $carteira = $this->dadosboleto["carteira"];
        //agencia e conta
        $agencia_codigo = $agencia."-". $this->modulo_11($agencia) ." / ". $conta ."-". $this->modulo_11($conta);
        //Zeros: usado quando convenio de 7 digitos
        $livre_zeros='000000';

        // Carteira 18 com Convênio de 8 dígitos
        if ($this->dadosboleto["formatacao_convenio"] == "8") {
                $convenio = $this->formata_numero($this->dadosboleto["convenio"],8,0,"convenio");
                // Nosso número de até 9 dígitos
                $nossonumero = $this->formata_numero($this->dadosboleto["nosso_numero"],9,0);
                $dv = $this->modulo_11("$codigobanco$nummoeda$fator_vencimento$valor$livre_zeros$convenio$nossonumero$carteira");
                $linha="$codigobanco$nummoeda$dv$fator_vencimento$valor$livre_zeros$convenio$nossonumero$carteira";
                //montando o nosso numero que aparecerá no boleto
                $nossonumero = $convenio . $nossonumero ."-". $this->modulo_11($convenio.$nossonumero);
        }

        // Carteira 18 com Convênio de 7 dígitos
        if ($this->dadosboleto["formatacao_convenio"] == "7") {
                $convenio = $this->formata_numero($this->dadosboleto["convenio"],7,0,"convenio");
                // Nosso número de até 10 dígitos
                $nossonumero = $this->formata_numero($this->dadosboleto["nosso_numero"],10,0);
                $dv = $this->modulo_11("$codigobanco$nummoeda$fator_vencimento$valor$livre_zeros$convenio$nossonumero$carteira");
                $linha="$codigobanco$nummoeda$dv$fator_vencimento$valor$livre_zeros$convenio$nossonumero$carteira";
                $nossonumero = $convenio.$nossonumero;
                //Não existe DV na composição do nosso-número para convênios de sete posições
        }

        // Carteira 18 com Convênio de 6 dígitos
        if ($this->dadosboleto["formatacao_convenio"] == "6") {
                $convenio = $this->formata_numero($this->dadosboleto["convenio"],6,0,"convenio");

                if ($this->dadosboleto["formatacao_nosso_numero"] == "1") {

                        // Nosso número de até 5 dígitos
                        $nossonumero = $this->formata_numero($this->dadosboleto["nosso_numero"],5,0);
                        $dv = $this->modulo_11("$codigobanco$nummoeda$fator_vencimento$valor$convenio$nossonumero$agencia$conta$carteira");
                        $linha = "$codigobanco$nummoeda$dv$fator_vencimento$valor$convenio$nossonumero$agencia$conta$carteira";
                        //montando o nosso numero que aparecerá no boleto
                        $nossonumero = $convenio . $nossonumero ."-". $this->modulo_11($convenio.$nossonumero);
                }

                if ($this->dadosboleto["formatacao_nosso_numero"] == "2") {

                        // Nosso número de até 17 dígitos
                        $nservico = "21";
                        $nossonumero = $this->formata_numero($this->dadosboleto["nosso_numero"],17,0);
                        $dv = $this->modulo_11("$codigobanco$nummoeda$fator_vencimento$valor$convenio$nossonumero$nservico");
                        $linha = "$codigobanco$nummoeda$dv$fator_vencimento$valor$convenio$nossonumero$nservico";
                }
        }


        $this->dadosboleto["codigo_barras"] = $linha;
        $this->dadosboleto["linha_digitavel"] = $this->monta_linha_digitavel($linha);
        $this->dadosboleto["agencia_codigo"] = $agencia_codigo;
        $this->dadosboleto["nosso_numero"] = $nossonumero;
        $this->dadosboleto["codigo_banco_com_dv"] = $codigo_banco_com_dv;

    }  

    private function dataJuliano($data)
    {
            $dia = (int)substr($data,1,2);
            $mes = (int)substr($data,3,2);
            $ano = (int)substr($data,6,4);
            $dataf = strtotime("$ano/$mes/$dia");
            $datai = strtotime(($ano-1).'/12/31');
            $dias  = (int)(($dataf - $datai)/(60*60*24));
      return str_pad($dias,3,'0',STR_PAD_LEFT).substr($data,9,4);
    }

    private function digitoVerificador_nossonumero($numero) {
            $resto2 = modulo_11($numero, 9, 1);
         $digito = 11 - $resto2;
         if ($digito == 10 || $digito == 11) {
            $dv = 0;
         } else {
            $dv = $digito;
         }
             return $dv;
    }


    private function digitoVerificador_barra($numero) {
            $resto2 = $this->modulo_11($numero, 9, 1);
         if ($resto2 == 0 || $resto2 == 1 || $resto2 == 10) {
            $dv = 1;
         } else {
                    $dv = 11 - $resto2;
         }
             return $dv;
    }

    private function formata_numero($numero,$loop,$insert,$tipo = "geral") {
	if ($tipo == "geral") {
		$numero = str_replace(",","",$numero);
		while(strlen($numero)<$loop){
			$numero = $insert . $numero;
		}
	}
	if ($tipo == "valor") {
		/*
		retira as virgulas
		formata o numero
		preenche com zeros
		*/
		$numero = str_replace(",","",$numero);
		while(strlen($numero)<$loop){
			$numero = $insert . $numero;
		}
	}
	if ($tipo == "convenio") {
		while(strlen($numero)<$loop){
			$numero = $numero . $insert;
		}
	}
	return $numero;
    }


    private function esquerda($entra,$comp){
            return substr($entra,0,$comp);
    }

    private function direita($entra,$comp){
            return substr($entra,strlen($entra)-$comp,$comp);
    }

    private function fator_vencimento($data) {
            $data = split("/",$data);
            $ano = $data[2];
            $mes = $data[1];
            $dia = $data[0];
            return(abs(($this->_dateToDays("1997","10","07")) - ($this->_dateToDays($ano, $mes, $dia))));
    }

    private function _dateToDays($year,$month,$day) {
        $century = substr($year, 0, 2);
        $year = substr($year, 2, 2);
        if ($month > 2) {
            $month -= 3;
        } else {
            $month += 9;
            if ($year) {
                $year--;
            } else {
                $year = 99;
                $century --;
            }
        }
        return ( floor((  146097 * $century)    /  4 ) +
                floor(( 1461 * $year)        /  4 ) +
                floor(( 153 * $month +  2) /  5 ) +
                    $day +  1721119);
    }

        public function modulo_10($num) {
                        $numtotal10 = 0;
                $fator = 2;

                // Separacao dos numeros
                for ($i = strlen($num); $i > 0; $i--) {
                    // pega cada numero isoladamente
                    $numeros[$i] = substr($num,$i-1,1);
                    // Efetua multiplicacao do numero pelo (falor 10)
                    // 2002-07-07 01:33:34 Macete para adequar ao Mod10 do Itaú
                    $temp = $numeros[$i] * $fator;
                    $temp0=0;
                    foreach (preg_split('//',$temp,-1,PREG_SPLIT_NO_EMPTY) as $k=>$v){ $temp0+=$v; }
                    $parcial10[$i] = $temp0; //$numeros[$i] * $fator;
                    // monta sequencia para soma dos digitos no (modulo 10)
                    $numtotal10 += $parcial10[$i];
                    if ($fator == 2) {
                        $fator = 1;
                    } else {
                        $fator = 2; // intercala fator de multiplicacao (modulo 10)
                    }
                }

                // várias linhas removidas, vide função original
                // Calculo do modulo 10
                $resto = $numtotal10 % 10;
                $digito = 10 - $resto;
                if ($resto == 0) {
                    $digito = 0;
                }

                return $digito;

        }

    protected function modulo_11($num, $base=9, $r=0)  {
        $soma = 0;
	$fator = 2;
	for ($i = strlen($num); $i > 0; $i--) {
		$numeros[$i] = substr($num,$i-1,1);
		$parcial[$i] = $numeros[$i] * $fator;
		$soma += $parcial[$i];
		if ($fator == $base) {
			$fator = 1;
		}
		$fator++;
	}
	if ($r == 0) {
		$soma *= 10;
		$digito = $soma % 11;

		//corrigido
		if ($digito == 10) {
			$digito = "X";
		}

		/*
		alterado por mim, Daniel Schultz

		Vamos explicar:

		O módulo 11 só gera os digitos verificadores do nossonumero,
		agencia, conta e digito verificador com codigo de barras (aquele que fica sozinho e triste na linha digitável)
		só que é foi um rolo...pq ele nao podia resultar em 0, e o pessoal do phpboleto se esqueceu disso...

		No BB, os dígitos verificadores podem ser X ou 0 (zero) para agencia, conta e nosso numero,
		mas nunca pode ser X ou 0 (zero) para a linha digitável, justamente por ser totalmente numérica.

		Quando passamos os dados para a função, fica assim:

		Agencia = sempre 4 digitos
		Conta = até 8 dígitos
		Nosso número = de 1 a 17 digitos

		A unica variável que passa 17 digitos é a da linha digitada, justamente por ter 43 caracteres

		Entao vamos definir ai embaixo o seguinte...

		se (strlen($num) == 43) { não deixar dar digito X ou 0 }
		*/

		if (strlen($num) == "43") {
			//então estamos checando a linha digitável
			if ($digito == "0" or $digito == "X" or $digito > 9) {
					$digito = 1;
			}
		}
		return $digito;
	}
	elseif ($r == 1){
		$resto = $soma % 11;
		return $resto;
	}
    }

    private function modulo_11_invertido($num)
    {
       $ftini = 2;
       $fator = $ftfim = 9;
       $soma = 0;

       for ($i = strlen($num); $i > 0; $i--)
       {
              $soma += substr($num,$i-1,1) * $fator;
              if(--$fator < $ftini)
                 $fator = $ftfim;
       }

        $digito = $soma % 11;

            if($digito > 9)
               $digito = 0;

            return $digito;
    }

    private function monta_linha_digitavel($linha)
    {

            // Posição 	Conteúdo
            // 1 a 3    Número do banco
            // 4        Código da Moeda - 9 para Real
            // 5        Digito verificador do Código de Barras
            // 6 a 19   Valor (12 inteiros e 2 decimais)
            // 20 a 44  Campo Livre definido por cada banco

            // 1. Campo - composto pelo código do banco, código da moéda, as cinco primeiras posições
            // do campo livre e DV (modulo10) deste campo
            $p1 = substr($linha, 0, 4);
            $p2 = substr($linha, 19, 5);
            $p3 = $this->modulo_10("$p1$p2");
            $p4 = "$p1$p2$p3";
            $p5 = substr($p4, 0, 5);
            $p6 = substr($p4, 5);
            $campo1 = "$p5.$p6";

            // 2. Campo - composto pelas posiçoes 6 a 15 do campo livre
            // e livre e DV (modulo10) deste campo
            $p1 = substr($linha, 24, 10);
            $p2 = $this->modulo_10($p1);
            $p3 = "$p1$p2";
            $p4 = substr($p3, 0, 5);
            $p5 = substr($p3, 5);
            $campo2 = "$p4.$p5";

            // 3. Campo composto pelas posicoes 16 a 25 do campo livre
            // e livre e DV (modulo10) deste campo
            $p1 = substr($linha, 34, 10);
            $p2 = $this->modulo_10($p1);
            $p3 = "$p1$p2";
            $p4 = substr($p3, 0, 5);
            $p5 = substr($p3, 5);
            $campo3 = "$p4.$p5";

            // 4. Campo - digito verificador do codigo de barras
            $campo4 = substr($linha, 4, 1);

            // 5. Campo composto pelo valor nominal pelo valor nominal do documento, sem
            // indicacao de zeros a esquerda e sem edicao (sem ponto e virgula). Quando se
            // tratar de valor zerado, a representacao deve ser 000 (tres zeros).
            $campo5 = substr($linha, 5, 14);

            return "$campo1 $campo2 $campo3 $campo4 $campo5";
    }

    private function geraCodigoBanco($numero) {
        $parte1 = substr($numero, 0, 3);
        $parte2 = $this->modulo_11($parte1);
        return $parte1 . "-" . $parte2;
    }
}

?>
